home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 98 / Skunkware 98.iso / src / mail / pine3.96.tar.gz / pine3.96.tar / pine3.96 / imap / ANSI / c-client / mtx.c < prev    next >
C/C++ Source or Header  |  1996-10-15  |  58KB  |  1,830 lines

  1. /*
  2.  * Program:    MTX mail routines
  3.  *
  4.  * Author:    Mark Crispin
  5.  *        Networks and Distributed Computing
  6.  *        Computing & Communications
  7.  *        University of Washington
  8.  *        Administration Building, AG-44
  9.  *        Seattle, WA  98195
  10.  *        Internet: MRC@CAC.Washington.EDU
  11.  *
  12.  * Date:    22 May 1990
  13.  * Last Edited:    15 October 1996
  14.  *
  15.  * Copyright 1996 by the University of Washington
  16.  *
  17.  *  Permission to use, copy, modify, and distribute this software and its
  18.  * documentation for any purpose and without fee is hereby granted, provided
  19.  * that the above copyright notice appears in all copies and that both the
  20.  * above copyright notice and this permission notice appear in supporting
  21.  * documentation, and that the name of the University of Washington not be
  22.  * used in advertising or publicity pertaining to distribution of the software
  23.  * without specific, written prior permission.  This software is made
  24.  * available "as is", and
  25.  * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
  26.  * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
  27.  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
  28.  * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
  29.  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  30.  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
  31.  * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
  32.  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  33.  *
  34.  */
  35.  
  36.  
  37. #include <stdio.h>
  38. #include <ctype.h>
  39. #include <errno.h>
  40. extern int errno;        /* just in case */
  41. #include "mail.h"
  42. #include "osdep.h"
  43. #include <pwd.h>
  44. #include <sys/stat.h>
  45. #include <sys/time.h>
  46. #include "mtx.h"
  47. #include "rfc822.h"
  48. #include "misc.h"
  49. #include "dummy.h"
  50.  
  51. /* Mtx mail routines */
  52.  
  53.  
  54. /* Driver dispatch used by MAIL */
  55.  
  56. DRIVER mtxdriver = {
  57.   "mtx",            /* driver name */
  58.   (DRIVER *) NIL,        /* next driver */
  59.   mtx_valid,            /* mailbox is valid for us */
  60.   mtx_parameters,        /* manipulate parameters */
  61.   mtx_find,            /* find mailboxes */
  62.   mtx_find_bboards,        /* find bboards */
  63.   mtx_find_all,            /* find all mailboxes */
  64.   mtx_find_all_bboards,        /* find all bboards */
  65.   mtx_subscribe,        /* subscribe to mailbox */
  66.   mtx_unsubscribe,        /* unsubscribe from mailbox */
  67.   mtx_subscribe_bboard,        /* subscribe to bboard */
  68.   mtx_unsubscribe_bboard,    /* unsubscribe from bboard */
  69.   mtx_create,            /* create mailbox */
  70.   mtx_delete,            /* delete mailbox */
  71.   mtx_rename,            /* rename mailbox */
  72.   mtx_open,            /* open mailbox */
  73.   mtx_close,            /* close mailbox */
  74.   mtx_fetchfast,        /* fetch message "fast" attributes */
  75.   mtx_fetchflags,        /* fetch message flags */
  76.   mtx_fetchstructure,        /* fetch message envelopes */
  77.   mtx_fetchheader,        /* fetch message header only */
  78.   mtx_fetchtext,        /* fetch message body only */
  79.   mtx_fetchbody,        /* fetch message body section */
  80.   mtx_setflag,            /* set message flag */
  81.   mtx_clearflag,        /* clear message flag */
  82.   mtx_search,            /* search for message based on criteria */
  83.   mtx_ping,            /* ping mailbox to see if still alive */
  84.   mtx_check,            /* check for new messages */
  85.   mtx_expunge,            /* expunge deleted messages */
  86.   mtx_copy,            /* copy messages to another mailbox */
  87.   mtx_move,            /* move messages to another mailbox */
  88.   mtx_append,            /* append string message to mailbox */
  89.   mtx_gc            /* garbage collect stream */
  90. };
  91.  
  92.                 /* prototype stream */
  93. MAILSTREAM mtxproto = {&mtxdriver};
  94.  
  95. /* Mtx mail validate mailbox
  96.  * Accepts: mailbox name
  97.  * Returns: our driver if name is valid, NIL otherwise
  98.  */
  99.  
  100. DRIVER *mtx_valid (char *name)
  101. {
  102.   char tmp[MAILTMPLEN];
  103.   return mtx_isvalid (name,tmp) ? &mtxdriver : NIL;
  104. }
  105.  
  106.  
  107. /* Mtx mail test for valid mailbox
  108.  * Accepts: mailbox name
  109.  * Returns: T if valid, NIL otherwise
  110.  */
  111.  
  112. int mtx_isvalid (char *name,char *tmp)
  113. {
  114.   int fd;
  115.   int ret = NIL;
  116.   char *s,file[MAILTMPLEN];
  117.   struct stat sbuf;
  118.   time_t tp[2];
  119.   errno = EINVAL;        /* assume invalid argument */
  120.                 /* if file, get its status */
  121.   if ((*name != '{') && !((*name == '*') && (name[1] == '{')) &&
  122.       (s = mtx_file (file,name)) && !stat (s,&sbuf)) {
  123.     if (!sbuf.st_size) {    /* allow empty file if INBOX */
  124.       if ((s = mailboxfile (tmp,name)) && !*s) ret = T;
  125.       else errno = 0;        /* empty file */
  126.     }
  127.     else if ((fd = open (file,O_RDONLY,NIL)) >= 0) {
  128.       memset (tmp,'\0',MAILTMPLEN);
  129.       if ((read (fd,tmp,64) >= 0) && (s = strchr (tmp,'\015')) &&
  130.       (s[1] == '\012')) {    /* valid format? */
  131.     *s = '\0';        /* tie off header */
  132.                 /* must begin with dd-mmm-yy" */
  133.     ret = (((tmp[2] == '-' && tmp[6] == '-') ||
  134.         (tmp[1] == '-' && tmp[5] == '-')) &&
  135.            (s = strchr (tmp+20,',')) && strchr (s+2,';')) ? T : NIL;
  136.       }
  137.       else errno = -1;        /* bogus format */
  138.       close (fd);        /* close the file */
  139.       tp[0] = sbuf.st_atime;    /* preserve atime and mtime */
  140.       tp[1] = sbuf.st_mtime;
  141.       utime (file,tp);        /* set the times */
  142.     }
  143.   }
  144.   return ret;            /* return what we should */
  145. }
  146.  
  147.  
  148. /* Mtx manipulate driver parameters
  149.  * Accepts: function code
  150.  *        function-dependent value
  151.  * Returns: function-dependent return value
  152.  */
  153.  
  154. void *mtx_parameters (long function,void *value)
  155. {
  156.   return NIL;
  157. }
  158.  
  159. /* Mtx mail find list of mailboxes
  160.  * Accepts: mail stream
  161.  *        pattern to search
  162.  */
  163.  
  164. void mtx_find (MAILSTREAM *stream,char *pat)
  165. {
  166.   if (stream) dummy_find (NIL,pat);
  167. }
  168.  
  169.  
  170. /* Mtx mail find list of bboards
  171.  * Accepts: mail stream
  172.  *        pattern to search
  173.  */
  174.  
  175. void mtx_find_bboards (MAILSTREAM *stream,char *pat)
  176. {
  177.   if (stream) dummy_find_bboards (NIL,pat);
  178. }
  179.  
  180.  
  181. /* Mtx mail find list of all mailboxes
  182.  * Accepts: mail stream
  183.  *        pattern to search
  184.  */
  185.  
  186. void mtx_find_all (MAILSTREAM *stream,char *pat)
  187. {
  188.   if (stream) dummy_find_all (NIL,pat);
  189. }
  190.  
  191.  
  192. /* Mtx mail find list of all bboards
  193.  * Accepts: mail stream
  194.  *        pattern to search
  195.  */
  196.  
  197. void mtx_find_all_bboards (MAILSTREAM *stream,char *pat)
  198. {
  199.   if (stream) dummy_find_all_bboards (NIL,pat);
  200. }
  201.  
  202. /* Mtx mail subscribe to mailbox
  203.  * Accepts: mail stream
  204.  *        mailbox to add to subscription list
  205.  * Returns: T on success, NIL on failure
  206.  */
  207.  
  208. long mtx_subscribe (MAILSTREAM *stream,char *mailbox)
  209. {
  210.   char tmp[MAILTMPLEN];
  211.   return sm_subscribe (mtx_file (tmp,mailbox));
  212. }
  213.  
  214.  
  215. /* Mtx mail unsubscribe to mailbox
  216.  * Accepts: mail stream
  217.  *        mailbox to delete from subscription list
  218.  * Returns: T on success, NIL on failure
  219.  */
  220.  
  221. long mtx_unsubscribe (MAILSTREAM *stream,char *mailbox)
  222. {
  223.   char tmp[MAILTMPLEN];
  224.   return sm_unsubscribe (mtx_file (tmp,mailbox));
  225. }
  226.  
  227.  
  228. /* Mtx mail subscribe to bboard
  229.  * Accepts: mail stream
  230.  *        bboard to add to subscription list
  231.  * Returns: T on success, NIL on failure
  232.  */
  233.  
  234. long mtx_subscribe_bboard (MAILSTREAM *stream,char *mailbox)
  235. {
  236.   return NIL;            /* never valid for Mtx */
  237. }
  238.  
  239.  
  240. /* Mtx mail unsubscribe to bboard
  241.  * Accepts: mail stream
  242.  *        bboard to delete from subscription list
  243.  * Returns: T on success, NIL on failure
  244.  */
  245.  
  246. long mtx_unsubscribe_bboard (MAILSTREAM *stream,char *mailbox)
  247. {
  248.   return NIL;            /* never valid for Mtx */
  249. }
  250.  
  251. /* Mtx mail create mailbox
  252.  * Accepts: MAIL stream
  253.  *        mailbox name to create
  254.  * Returns: T on success, NIL on failure
  255.  */
  256.  
  257. long mtx_create (MAILSTREAM *stream,char *mailbox)
  258. {
  259.   return dummy_create (stream,mailbox);
  260. }
  261.  
  262.  
  263. /* Mtx mail delete mailbox
  264.  * Accepts: MAIL stream
  265.  *        mailbox name to delete
  266.  * Returns: T on success, NIL on failure
  267.  */
  268.  
  269. long mtx_delete (MAILSTREAM *stream,char *mailbox)
  270. {
  271.   return mtx_rename (stream,mailbox,NIL);
  272. }
  273.  
  274. /* Mtx mail rename mailbox
  275.  * Accepts: MAIL stream
  276.  *        old mailbox name
  277.  *        new mailbox name (or NIL for delete)
  278.  * Returns: T on success, NIL on failure
  279.  */
  280.  
  281. long mtx_rename (MAILSTREAM *stream,char *old,char *new)
  282. {
  283.   long ret = T;
  284.   char tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN];
  285.   int ld;
  286.   int fd = open (mtx_file (file,old),O_RDWR,NIL);
  287.                 /* lock out non c-client applications */
  288.   if (fd < 0) {            /* open mailbox */
  289.     sprintf (tmp,"Can't open mailbox %s: %s",old,strerror (errno));
  290.     mm_log (tmp,ERROR);
  291.     return NIL;
  292.   }
  293.                 /* get exclusive parse/append permission */
  294.   if ((ld = mtx_lock (fd,lock,LOCK_EX)) < 0) {
  295.     mm_log ("Unable to lock rename mailbox",ERROR);
  296.     return NIL;
  297.   }
  298.                 /* lock out other users */
  299.   if (flock (fd,LOCK_EX|LOCK_NB)) {
  300.     close (fd);            /* couldn't lock, give up on it then */
  301.     sprintf (tmp,"Mailbox %s is in use by another process",old);
  302.     mm_log (tmp,ERROR);
  303.     mtx_unlock (ld,lock);    /* release exclusive parse/append permission */
  304.     return NIL;
  305.   }
  306.                 /* do the rename or delete operation */
  307.   if (new ? rename (file,mtx_file (tmp,new)) : unlink (file)) {
  308.     sprintf (tmp,"Can't %s mailbox %s: %s",new ? "rename" : "delete",old,
  309.          strerror (errno));
  310.     mm_log (tmp,ERROR);
  311.     ret = NIL;            /* set failure */
  312.   }
  313.   flock (fd,LOCK_UN);        /* release lock on the file */
  314.   mtx_unlock (ld,lock);    /* release exclusive parse/append permission */
  315.   close (fd);            /* close the file */
  316.   return ret;            /* return success */
  317. }
  318.  
  319. /* Mtx mail open
  320.  * Accepts: stream to open
  321.  * Returns: stream on success, NIL on failure
  322.  */
  323.  
  324. MAILSTREAM *mtx_open (MAILSTREAM *stream)
  325. {
  326.   int fd,ld;
  327.   char tmp[MAILTMPLEN];
  328.                 /* return prototype for OP_PROTOTYPE call */
  329.   if (!stream) return user_flags (&mtxproto);
  330.   if (LOCAL) {            /* close old file if stream being recycled */
  331.     mtx_close (stream);    /* dump and save the changes */
  332.     stream->dtb = &mtxdriver;    /* reattach this driver */
  333.     mail_free_cache (stream);    /* clean up cache */
  334.   }
  335.   user_flags (stream);        /* set up user flags */
  336.                 /* force readonly if bboard */
  337.   if (*stream->mailbox == '*') stream->rdonly = T;
  338.   if (stream->rdonly ||
  339.       (fd = open (mtx_file (tmp,stream->mailbox),O_RDWR,NIL)) < 0) {
  340.     if ((fd = open (mtx_file (tmp,stream->mailbox),O_RDONLY,NIL)) < 0) {
  341.       sprintf (tmp,"Can't open mailbox: %s",strerror (errno));
  342.       mm_log (tmp,ERROR);
  343.       return NIL;
  344.     }
  345.     else if (!stream->rdonly) {    /* got it, but readonly */
  346.       mm_log ("Can't get write access to mailbox, access is readonly",WARN);
  347.       stream->rdonly = T;
  348.     }
  349.   }
  350.   stream->local = fs_get (sizeof (MTXLOCAL));
  351.   LOCAL->buf = (char *) fs_get (MAXMESSAGESIZE + 1);
  352.   LOCAL->buflen = MAXMESSAGESIZE;
  353.                 /* note if an INBOX or not */
  354.   LOCAL->inbox = !strcmp (ucase (strcpy (LOCAL->buf,stream->mailbox)),"INBOX");
  355.   if (*stream->mailbox != '*') {/* canonicalize the stream mailbox name */
  356.     fs_give ((void **) &stream->mailbox);
  357.     stream->mailbox = cpystr (tmp);
  358.   }
  359.                 /* get shared parse permission */
  360.   if ((ld = mtx_lock (fd,tmp,LOCK_SH)) < 0) {
  361.     mm_log ("Unable to lock open mailbox",ERROR);
  362.     return NIL;
  363.   }
  364.   flock(LOCAL->fd = fd,LOCK_SH);/* bind and lock the file */
  365.   mtx_unlock (ld,tmp);        /* release shared parse permission */
  366.   LOCAL->filesize = 0;        /* initialize parsed file size */
  367.   LOCAL->filetime = 0;        /* time not set up yet */
  368.   LOCAL->mustcheck = LOCAL->shouldcheck = NIL;
  369.   stream->sequence++;        /* bump sequence number */
  370.                 /* parse mailbox */
  371.   stream->nmsgs = stream->recent = 0;
  372.   if (mtx_ping (stream) && !stream->nmsgs)
  373.     mm_log ("Mailbox is empty",(long) NIL);
  374.   if (!LOCAL) return NIL;    /* failure if stream died */
  375.   return stream;        /* return stream to caller */
  376. }
  377.  
  378. /* Mtx mail close
  379.  * Accepts: MAIL stream
  380.  */
  381.  
  382. void mtx_close (MAILSTREAM *stream)
  383. {
  384.   if (stream && LOCAL) {    /* only if a file is open */
  385.     flock (LOCAL->fd,LOCK_UN);    /* unlock local file */
  386.     close (LOCAL->fd);        /* close the local file */
  387.                 /* free local text buffer */
  388.     if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
  389.                 /* nuke the local data */
  390.     fs_give ((void **) &stream->local);
  391.     stream->dtb = NIL;        /* log out the DTB */
  392.   }
  393. }
  394.  
  395.  
  396. /* Mtx mail fetch fast information
  397.  * Accepts: MAIL stream
  398.  *        sequence
  399.  */
  400.  
  401. void mtx_fetchfast (MAILSTREAM *stream,char *sequence)
  402. {
  403.   return;            /* no-op for local mail */
  404. }
  405.  
  406.  
  407. /* Mtx mail fetch flags
  408.  * Accepts: MAIL stream
  409.  *        sequence
  410.  */
  411.  
  412. void mtx_fetchflags (MAILSTREAM *stream,char *sequence)
  413. {
  414.   long i;
  415.                 /* ping mailbox, get new status for messages */
  416.   if (mtx_ping (stream) && mail_sequence (stream,sequence))
  417.     for (i = 1; i <= stream->nmsgs; i++) 
  418.       if (mail_elt (stream,i)->sequence) mtx_elt (stream,i);
  419. }
  420.  
  421. /* Mtx mail fetch structure
  422.  * Accepts: MAIL stream
  423.  *        message # to fetch
  424.  *        pointer to return body
  425.  * Returns: envelope of this message, body returned in body value
  426.  *
  427.  * Fetches the "fast" information as well
  428.  */
  429.  
  430. ENVELOPE *mtx_fetchstructure (MAILSTREAM *stream,long msgno,BODY **body)
  431. {
  432.   LONGCACHE *lelt;
  433.   ENVELOPE **env;
  434.   BODY **b;
  435.   STRING bs;
  436.   char *hdr,*text;
  437.   unsigned long hdrsize;
  438.   unsigned long hdrpos = mtx_header (stream,msgno,&hdrsize);
  439.   unsigned long textsize = body ? mtx_size (stream,msgno) - hdrsize : 0;
  440.   unsigned long i = max (hdrsize,textsize);
  441.   if (stream->scache) {        /* short cache */
  442.     if (msgno != stream->msgno){/* flush old poop if a different message */
  443.       mail_free_envelope (&stream->env);
  444.       mail_free_body (&stream->body);
  445.     }
  446.     stream->msgno = msgno;
  447.     env = &stream->env;        /* get pointers to envelope and body */
  448.     b = &stream->body;
  449.   }
  450.   else {            /* long cache */
  451.     lelt = mail_lelt (stream,msgno);
  452.     env = &lelt->env;        /* get pointers to envelope and body */
  453.     b = &lelt->body;
  454.   }
  455.   if ((body && !*b) || !*env) {    /* have the poop we need? */
  456.     mail_free_envelope (env);    /* flush old envelope and body */
  457.     mail_free_body (b);
  458.     if (i > LOCAL->buflen) {    /* make sure enough buffer space */
  459.       fs_give ((void **) &LOCAL->buf);
  460.       LOCAL->buf = (char *) fs_get ((LOCAL->buflen = i) + 1);
  461.     }
  462.                 /* get to header position */
  463.     lseek (LOCAL->fd,hdrpos,L_SET);
  464.                 /* read the header and text */
  465.     read (LOCAL->fd,hdr = (char *) fs_get (hdrsize + 1),hdrsize);
  466.     read (LOCAL->fd,text = (char *) fs_get (textsize + 1),textsize);
  467.     hdr[hdrsize] = '\0';    /* make sure tied off */
  468.     text[textsize] = '\0';    /* make sure tied off */
  469.     INIT (&bs,mail_string,(void *) text,textsize);
  470.                 /* parse envelope and body */
  471.     rfc822_parse_msg (env,body ? b : NIL,hdr,hdrsize,&bs,mylocalhost (),
  472.               LOCAL->buf);
  473.     fs_give ((void **) &text);
  474.     fs_give ((void **) &hdr);
  475.   }
  476.   if (body) *body = *b;        /* return the body */
  477.   return *env;            /* return the envelope */
  478. }
  479.  
  480. /* Mtx mail fetch message header
  481.  * Accepts: MAIL stream
  482.  *        message # to fetch
  483.  * Returns: message header in RFC822 format
  484.  */
  485.  
  486. char *mtx_fetchheader (MAILSTREAM *stream,long msgno)
  487. {
  488.   unsigned long hdrsize;
  489.   unsigned long hdrpos = mtx_header (stream,msgno,&hdrsize);
  490.   if (LOCAL->buflen < hdrsize) {/* fs_resize would do an unnecessary copy */
  491.     fs_give ((void **) &LOCAL->buf);
  492.     LOCAL->buf = (char *) fs_get ((LOCAL->buflen = hdrsize) + 1);
  493.   }
  494.   LOCAL->buf[hdrsize] = '\0';    /* tie off string */
  495.                 /* get to header position */
  496.   lseek (LOCAL->fd,hdrpos,L_SET);
  497.                 /* slurp the data */
  498.   read (LOCAL->fd,LOCAL->buf,hdrsize);
  499.   return LOCAL->buf;
  500. }
  501.  
  502.  
  503. /* Mtx mail fetch message text (body only)
  504.  * Accepts: MAIL stream
  505.  *        message # to fetch
  506.  * Returns: message text in RFC822 format
  507.  */
  508.  
  509. char *mtx_fetchtext (MAILSTREAM *stream,long msgno)
  510. {
  511.   unsigned long hdrsize;
  512.   unsigned long hdrpos = mtx_header (stream,msgno,&hdrsize);
  513.   unsigned long textsize = mtx_size (stream,msgno) - hdrsize;
  514.   if (LOCAL->buflen<textsize) {    /* fs_resize would do an unnecessary copy */
  515.     fs_give ((void **) &LOCAL->buf);
  516.     LOCAL->buf = (char *) fs_get ((LOCAL->buflen = textsize) + 1);
  517.   }
  518.   LOCAL->buf[textsize] = '\0';    /* tie off string */
  519.                 /* mark message as seen */
  520.   mtx_elt (stream,msgno)->seen = T;
  521.                 /* recalculate status */
  522.   mtx_update_status (stream,msgno,T);
  523.                 /* get to text position */
  524.   lseek (LOCAL->fd,hdrpos + hdrsize,L_SET);
  525.                 /* slurp the data */
  526.   read (LOCAL->fd,LOCAL->buf,textsize);
  527.   return LOCAL->buf;
  528. }
  529.  
  530. /* Mtx fetch message body as a structure
  531.  * Accepts: Mail stream
  532.  *        message # to fetch
  533.  *        section specifier
  534.  *        pointer to length
  535.  * Returns: pointer to section of message body
  536.  */
  537.  
  538. char *mtx_fetchbody (MAILSTREAM *stream,long m,char *s,unsigned long *len)
  539. {
  540.   BODY *b;
  541.   PART *pt;
  542.   char *t;
  543.   unsigned long i;
  544.   unsigned long base;
  545.   unsigned long offset = 0;
  546.   unsigned long hdrpos = mtx_header (stream,m,&base);
  547.   MESSAGECACHE *elt = mtx_elt (stream,m);
  548.                 /* make sure have a body */
  549.   if (!(mtx_fetchstructure (stream,m,&b) && b && s && *s &&
  550.     ((i = strtol (s,&s,10)) > 0))) return NIL;
  551.   do {                /* until find desired body part */
  552.                 /* multipart content? */
  553.     if (b->type == TYPEMULTIPART) {
  554.       pt = b->contents.part;    /* yes, find desired part */
  555.       while (--i && (pt = pt->next));
  556.       if (!pt) return NIL;    /* bad specifier */
  557.                 /* note new body, check valid nesting */
  558.       if (((b = &pt->body)->type == TYPEMULTIPART) && !*s) return NIL;
  559.       offset = pt->offset;    /* get new offset */
  560.     }
  561.     else if (i != 1) return NIL;/* otherwise must be section 1 */
  562.                 /* need to go down further? */
  563.     if (i = *s) switch (b->type) {
  564.     case TYPEMESSAGE:        /* embedded message, calculate new base */
  565.       offset = b->contents.msg.offset;
  566.       b = b->contents.msg.body;    /* get its body, drop into multipart case */
  567.     case TYPEMULTIPART:        /* multipart, get next section */
  568.       if ((*s++ == '.') && (i = strtol (s,&s,10)) > 0) break;
  569.     default:            /* bogus subpart specification */
  570.       return NIL;
  571.     }
  572.   } while (i);
  573.                 /* lose if body bogus */
  574.   if ((!b) || b->type == TYPEMULTIPART) return NIL;
  575.   elt->seen = T;        /* mark message as seen */
  576.                 /* recalculate status */
  577.   mtx_update_status (stream,m,T);
  578.                 /* move to that place in the data */
  579.   lseek (LOCAL->fd,hdrpos + base + offset,L_SET);
  580.                 /* fs_resize would do an unnecessary copy */
  581.   if (LOCAL->buflen < b->size.ibytes) {
  582.     fs_give ((void **) &LOCAL->buf);
  583.     LOCAL->buf = (char *) fs_get ((LOCAL->buflen = b->size.ibytes) + 1);
  584.   }
  585.                 /* tie off string */
  586.   LOCAL->buf[b->size.ibytes] = '\0';
  587.   read (LOCAL->fd,LOCAL->buf,b->size.ibytes);
  588.   *len = b->size.ibytes;    /* return length */
  589.   return LOCAL->buf;
  590. }
  591.  
  592. /* Mtx locate header for a message
  593.  * Accepts: MAIL stream
  594.  *        message number
  595.  *        pointer to returned header size
  596.  * Returns: position of header in file
  597.  */
  598.  
  599. unsigned long mtx_header (MAILSTREAM *stream,long msgno,unsigned long *size)
  600. {
  601.   long siz;
  602.   long i = 0;
  603.   int q = 0;
  604.   char *s,tmp[MAILTMPLEN];
  605.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  606.   long pos = elt->data1 + (elt->data2 >> 24);
  607.                 /* is size known? */
  608.   if (!(*size = (elt->data2 & (unsigned long) 0xffffff))) {
  609.     lseek (LOCAL->fd,pos,L_SET);/* get to header position */
  610.                 /* search message for CRLF CRLF */
  611.     for (siz = 0; siz < elt->rfc822_size; siz++) {
  612.                 /* read another buffer as necessary */
  613.       if (--i <= 0)        /* buffer empty? */
  614.     if (read (LOCAL->fd,s = tmp,
  615.            i = min (elt->rfc822_size - siz,(long) MAILTMPLEN)) < 0)
  616.       return pos;        /* I/O error? */
  617.       switch (q) {        /* sniff at buffer */
  618.       case 0:            /* first character */
  619.     q = (*s++ == '\015') ? 1 : 0;
  620.     break;
  621.       case 1:            /* second character */
  622.     q = (*s++ == '\012') ? 2 : 0;
  623.     break;
  624.       case 2:            /* third character */
  625.     q = (*s++ == '\015') ? 3 : 0;
  626.     break;
  627.       case 3:            /* fourth character */
  628.     if (*s++ == '\012') {    /* have the sequence? */
  629.                 /* yes, note for later */
  630.       elt->data2 |= (*size = siz);
  631.       return pos;        /* return to caller */
  632.     }
  633.     q = 0;            /* lost... */
  634.     break;
  635.       }
  636.     }
  637.   }
  638.   return pos;            /* have position */
  639. }
  640.  
  641. /* Mtx mail set flag
  642.  * Accepts: MAIL stream
  643.  *        sequence
  644.  *        flag(s)
  645.  */
  646.  
  647. void mtx_setflag (MAILSTREAM *stream,char *sequence,char *flag)
  648. {
  649.   MESSAGECACHE *elt;
  650.   long i;
  651.   unsigned long uf;
  652.   short f;
  653.   struct stat sbuf;
  654.   if (LOCAL->filetime && !LOCAL->shouldcheck) {
  655.     fstat (LOCAL->fd,&sbuf);    /* get current write time */
  656.     if (LOCAL->filetime < sbuf.st_mtime) LOCAL->shouldcheck = T;
  657.   }
  658.                 /* no-op if no flags to modify */
  659.   if (!((f = mtx_getflags (stream,flag,&uf)) || uf)) return;
  660.                 /* get sequence and loop on it */
  661.   if (mail_sequence (stream,sequence)) for (i = 1; i <= stream->nmsgs; i++)
  662.     if ((elt = mail_elt (stream,i))->sequence) {
  663.       mtx_elt (stream,i);    /* set all requested flags */
  664.       if (f&fSEEN) elt->seen = T;
  665.       if (f&fDELETED) elt->deleted = T;
  666.       if (f&fFLAGGED) elt->flagged = T;
  667.       if (f&fANSWERED) elt->answered = T;
  668.       elt->user_flags |= uf;
  669.                 /* recalculate status */
  670.       mtx_update_status (stream,i,NIL);
  671.     }
  672.   if (!stream->rdonly) {    /* make sure the update takes */
  673.     fsync (LOCAL->fd);
  674.     fstat (LOCAL->fd,&sbuf);    /* get current write time */
  675.     LOCAL->filetime = sbuf.st_mtime;
  676.   }
  677. }
  678.  
  679. /* Mtx mail clear flag
  680.  * Accepts: MAIL stream
  681.  *        sequence
  682.  *        flag(s)
  683.  */
  684.  
  685. void mtx_clearflag (MAILSTREAM *stream,char *sequence,char *flag)
  686. {
  687.   MESSAGECACHE *elt;
  688.   long i;
  689.   unsigned long uf;
  690.   short f;
  691.   struct stat sbuf;
  692.   if (LOCAL->filetime && !LOCAL->shouldcheck) {
  693.     fstat (LOCAL->fd,&sbuf);    /* get current write time */
  694.     if (LOCAL->filetime < sbuf.st_mtime) LOCAL->shouldcheck = T;
  695.   }
  696.                 /* no-op if no flags to modify */
  697.   if (!((f = mtx_getflags (stream,flag,&uf)) || uf)) return;
  698.                 /* get sequence and loop on it */
  699.   if (mail_sequence (stream,sequence)) for (i = 1; i <= stream->nmsgs; i++)
  700.     if ((elt = mail_elt (stream,i))->sequence) {
  701.       mtx_elt (stream,i);    /* clear all requested flags */
  702.       if (f&fSEEN) elt->seen = NIL;
  703.       if (f&fDELETED) elt->deleted = NIL;
  704.       if (f&fFLAGGED) elt->flagged = NIL;
  705.       if (f&fANSWERED) elt->answered = NIL;
  706.       elt->user_flags &= ~uf;
  707.                 /* recalculate status */
  708.       mtx_update_status (stream,i,NIL);
  709.     }
  710.   if (!stream->rdonly) {    /* make sure the update takes */
  711.     fsync (LOCAL->fd);
  712.     fstat (LOCAL->fd,&sbuf);    /* get current write time */
  713.     LOCAL->filetime = sbuf.st_mtime;
  714.   }
  715. }
  716.  
  717. /* Mtx mail search for messages
  718.  * Accepts: MAIL stream
  719.  *        search criteria
  720.  */
  721.  
  722. void mtx_search (MAILSTREAM *stream,char *criteria)
  723. {
  724.   long i,n;
  725.   char *d;
  726.   search_t f;
  727.                 /* initially all searched */
  728.   for (i = 1; i <= stream->nmsgs; ++i) mtx_elt (stream,i)->searched = T;
  729.                 /* get first criterion */
  730.   if (criteria && (criteria = strtok (criteria," "))) {
  731.                 /* for each criterion */
  732.     for (; criteria; (criteria = strtok (NIL," "))) {
  733.       f = NIL; d = NIL; n = 0;    /* init then scan the criterion */
  734.       switch (*ucase (criteria)) {
  735.       case 'A':            /* possible ALL, ANSWERED */
  736.     if (!strcmp (criteria+1,"LL")) f = mtx_search_all;
  737.     else if (!strcmp (criteria+1,"NSWERED")) f = mtx_search_answered;
  738.     break;
  739.       case 'B':            /* possible BCC, BEFORE, BODY */
  740.     if (!strcmp (criteria+1,"CC"))
  741.       f = mtx_search_string (mtx_search_bcc,&d,&n);
  742.     else if (!strcmp (criteria+1,"EFORE"))
  743.       f = mtx_search_date (mtx_search_before,&n);
  744.     else if (!strcmp (criteria+1,"ODY"))
  745.       f = mtx_search_string (mtx_search_body,&d,&n);
  746.     break;
  747.       case 'C':            /* possible CC */
  748.     if (!strcmp (criteria+1,"C")) 
  749.       f = mtx_search_string (mtx_search_cc,&d,&n);
  750.     break;
  751.       case 'D':            /* possible DELETED */
  752.     if (!strcmp (criteria+1,"ELETED")) f = mtx_search_deleted;
  753.     break;
  754.       case 'F':            /* possible FLAGGED, FROM */
  755.     if (!strcmp (criteria+1,"LAGGED")) f = mtx_search_flagged;
  756.     else if (!strcmp (criteria+1,"ROM"))
  757.       f = mtx_search_string (mtx_search_from,&d,&n);
  758.     break;
  759.       case 'K':            /* possible KEYWORD */
  760.     if (!strcmp (criteria+1,"EYWORD"))
  761.       f = mtx_search_flag (mtx_search_keyword,&n,stream);
  762.     break;
  763.       case 'N':            /* possible NEW */
  764.     if (!strcmp (criteria+1,"EW")) f = mtx_search_new;
  765.     break;
  766.  
  767.       case 'O':            /* possible OLD, ON */
  768.     if (!strcmp (criteria+1,"LD")) f = mtx_search_old;
  769.     else if (!strcmp (criteria+1,"N"))
  770.       f = mtx_search_date (mtx_search_on,&n);
  771.     break;
  772.       case 'R':            /* possible RECENT */
  773.     if (!strcmp (criteria+1,"ECENT")) f = mtx_search_recent;
  774.     break;
  775.       case 'S':            /* possible SEEN, SINCE, SUBJECT */
  776.     if (!strcmp (criteria+1,"EEN")) f = mtx_search_seen;
  777.     else if (!strcmp (criteria+1,"INCE"))
  778.       f = mtx_search_date (mtx_search_since,&n);
  779.     else if (!strcmp (criteria+1,"UBJECT"))
  780.       f = mtx_search_string (mtx_search_subject,&d,&n);
  781.     break;
  782.       case 'T':            /* possible TEXT, TO */
  783.     if (!strcmp (criteria+1,"EXT"))
  784.       f = mtx_search_string (mtx_search_text,&d,&n);
  785.     else if (!strcmp (criteria+1,"O"))
  786.       f = mtx_search_string (mtx_search_to,&d,&n);
  787.     break;
  788.       case 'U':            /* possible UN* */
  789.     if (criteria[1] == 'N') {
  790.       if (!strcmp (criteria+2,"ANSWERED")) f = mtx_search_unanswered;
  791.       else if (!strcmp (criteria+2,"DELETED")) f = mtx_search_undeleted;
  792.       else if (!strcmp (criteria+2,"FLAGGED")) f = mtx_search_unflagged;
  793.       else if (!strcmp (criteria+2,"KEYWORD"))
  794.         f = mtx_search_flag (mtx_search_unkeyword,&n,stream);
  795.       else if (!strcmp (criteria+2,"SEEN")) f = mtx_search_unseen;
  796.     }
  797.     break;
  798.       default:            /* we will barf below */
  799.     break;
  800.       }
  801.       if (!f) {            /* if can't determine any criteria */
  802.     sprintf (LOCAL->buf,"Unknown search criterion: %.80s",criteria);
  803.     mm_log (LOCAL->buf,ERROR);
  804.     return;
  805.       }
  806.                 /* run the search criterion */
  807.       for (i = 1; i <= stream->nmsgs; ++i)
  808.     if (mail_elt (stream,i)->searched && !(*f) (stream,i,d,n))
  809.       mail_elt (stream,i)->searched = NIL;
  810.     }
  811.                 /* report search results to main program */
  812.     for (i = 1; i <= stream->nmsgs; ++i)
  813.       if (mail_elt (stream,i)->searched) mail_searched (stream,i);
  814.   }
  815. }
  816.  
  817. /* Mtx mail ping mailbox
  818.  * Accepts: MAIL stream
  819.  * Returns: T if stream still alive, NIL if not
  820.  */
  821.  
  822. long mtx_ping (MAILSTREAM *stream)
  823. {
  824.   long i = 1;
  825.   long r = NIL;
  826.   int ld; 
  827.   char lock[MAILTMPLEN];
  828.   struct stat sbuf;
  829.   if (stream && LOCAL) {    /* only if stream already open */
  830.     if (LOCAL->filetime && !(LOCAL->mustcheck || LOCAL->shouldcheck)) {
  831.       fstat (LOCAL->fd,&sbuf);    /* get current write time */
  832.       if (LOCAL->filetime < sbuf.st_mtime) LOCAL->shouldcheck = T;
  833.     }
  834.                 /* check for changed message status */
  835.     if (LOCAL->mustcheck || LOCAL->shouldcheck) {
  836.       if (LOCAL->shouldcheck)    /* babble when we do this unilaterally */
  837.     mm_notify (stream,"[CHECK] Checking for flag updates",NIL);
  838.       while (i <= stream->nmsgs) mtx_elt (stream,i++);
  839.       LOCAL->mustcheck = LOCAL->shouldcheck = NIL;
  840.     }
  841.                 /* get shared parse/append permission */
  842.     if ((ld = mtx_lock (LOCAL->fd,lock,LOCK_SH)) >= 0) {
  843.                 /* parse resulting mailbox */
  844.       r = (mtx_parse (stream)) ? T : NIL;
  845.       mtx_unlock (ld,lock);    /* release shared parse/append permission */
  846.     }
  847.                 /* snarf if this is a read-write inbox */
  848.     if (stream && LOCAL && LOCAL->inbox && !stream->rdonly) {
  849.       mtx_snarf (stream);
  850.                 /* get shared parse/append permission */
  851.       if ((ld = mtx_lock (LOCAL->fd,lock,LOCK_SH)) >= 0) {
  852.                 /* parse resulting mailbox */
  853.     r = (mtx_parse (stream)) ? T : NIL;
  854.     mtx_unlock (ld,lock);    /* release shared parse/append permission */
  855.       }
  856.     }
  857.   }
  858.   return r;            /* return result of the parse */
  859. }
  860.  
  861.  
  862. /* Mtx mail check mailbox (reparses status too)
  863.  * Accepts: MAIL stream
  864.  */
  865.  
  866. void mtx_check (MAILSTREAM *stream)
  867. {
  868.                 /* mark that a check is desired */
  869.   if (LOCAL) LOCAL->mustcheck = T;
  870.   if (mtx_ping (stream)) mm_log ("Check completed",(long) NIL);
  871. }
  872.  
  873. /* Mtx mail snarf messages from system inbox
  874.  * Accepts: MAIL stream
  875.  */
  876.  
  877. void mtx_snarf (MAILSTREAM *stream)
  878. {
  879.   long i = 0;
  880.   long r,j;
  881.   struct stat sbuf;
  882.   struct iovec iov[3];
  883.   char lock[MAILTMPLEN];
  884.   MESSAGECACHE *elt;
  885.   MAILSTREAM *sysibx = NIL;
  886.   int ld;
  887.                 /* give up if can't get exclusive permission */
  888.   if ((!strcmp (sysinbox (),stream->mailbox)) ||
  889.       ((ld = mtx_lock (LOCAL->fd,lock,LOCK_EX)) < 0)) return;
  890.   mm_critical (stream);        /* go critical */
  891.   stat (sysinbox (),&sbuf);    /* see if anything there */
  892.   if (sbuf.st_size) {        /* non-empty? */
  893.     fstat (LOCAL->fd,&sbuf);    /* yes, get current file size */
  894.                 /* sizes match and can get sysibx mailbox? */
  895.     if ((sbuf.st_size == LOCAL->filesize) && 
  896.     (sysibx = mail_open (sysibx,sysinbox (),OP_SILENT)) &&
  897.     (!sysibx->rdonly) && (r = sysibx->nmsgs)) {
  898.                 /* yes, go to end of file in our mailbox */
  899.       lseek (LOCAL->fd,sbuf.st_size,L_SET);
  900.                 /* for each message in sysibx mailbox */
  901.       while (r && (++i <= sysibx->nmsgs)) {
  902.                 /* snarf message from Berkeley mailbox */
  903.     iov[1].iov_base = cpystr (mail_fetchheader (sysibx,i));
  904.     iov[1].iov_len = strlen (iov[1].iov_base);
  905.     iov[2].iov_base = mail_fetchtext (sysibx,i);
  906.     iov[2].iov_len = strlen (iov[2].iov_base);
  907.                 /* calculate header line */
  908.     mail_date ((iov[0].iov_base = LOCAL->buf),elt = mail_elt (sysibx,i));
  909.     sprintf(LOCAL->buf + strlen (LOCAL->buf),",%ld;0000000000%02o\015\012",
  910.         iov[1].iov_len + iov[2].iov_len,(fOLD * !(elt->recent)) +
  911.         (fSEEN * elt->seen) + (fDELETED * elt->deleted) +
  912.         (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered));
  913.     iov[0].iov_len = strlen (iov[0].iov_base);
  914.                 /* copy message to new mailbox */
  915.     if ((writev (LOCAL->fd,iov,3) < 0) || fsync (LOCAL->fd)) {
  916.       sprintf (LOCAL->buf,"Can't copy new mail: %s",strerror (errno));
  917.       mm_log (LOCAL->buf,ERROR);
  918.       ftruncate (LOCAL->fd,sbuf.st_size);
  919.       r = 0;        /* flag that we have lost big */
  920.     }
  921.     fs_give ((void **) &iov[1].iov_base);
  922.       }
  923.       fstat (LOCAL->fd,&sbuf);    /* yes, get current file size */
  924.       LOCAL->filetime = sbuf.st_mtime;
  925.       if (r) {            /* delete all the messages we copied */
  926.     for (i = 1; i <= r; i++) mail_elt (sysibx,i)->deleted = T;
  927.     mail_expunge (sysibx);    /* now expunge all those messages */
  928.       }
  929.  
  930.     }
  931.     if (sysibx) mail_close (sysibx);
  932.   }
  933.   mm_nocritical (stream);    /* release critical */
  934.   mtx_unlock (ld,lock);        /* release exclusive parse/append permission */
  935. }
  936.  
  937. /* Mtx mail expunge mailbox
  938.  * Accepts: MAIL stream
  939.  */
  940.  
  941. void mtx_expunge (MAILSTREAM *stream)
  942. {
  943.   struct stat sbuf;
  944.   off_t pos = 0;
  945.   int ld;
  946.   unsigned long i = 1;
  947.   unsigned long j,k,m,recent;
  948.   unsigned long n = 0;
  949.   unsigned long delta = 0;
  950.   char lock[MAILTMPLEN];
  951.   MESSAGECACHE *elt;
  952.                 /* do nothing if stream dead */
  953.   if (!mtx_ping (stream)) return;
  954.   if (stream->rdonly) {        /* won't do on readonly files! */
  955.     mm_log ("Expunge ignored on readonly mailbox",WARN);
  956.     return;
  957.   }
  958.   if (LOCAL->filetime && !LOCAL->shouldcheck) {
  959.     fstat (LOCAL->fd,&sbuf);    /* get current write time */
  960.     if (LOCAL->filetime < sbuf.st_mtime) LOCAL->shouldcheck = T;
  961.   }
  962.   /* The cretins who designed flock() created a window of vulnerability in
  963.    * upgrading locks from shared to exclusive or downgrading from exclusive
  964.    * to shared.  Rather than maintain the lock at shared status at a minimum,
  965.    * flock() actually *releases* the former lock.  Obviously they never talked
  966.    * to any database guys.  Fortunately, we have the parse/append permission
  967.    * lock.  If we require this lock before going exclusive on the mailbox,
  968.    * another process can not sneak in and steal the exclusive mailbox lock on
  969.    * us, because it will block on trying to get parse/append permission first.
  970.    */
  971.                 /* get exclusive parse/append permission */
  972.   if ((ld = mtx_lock (LOCAL->fd,lock,LOCK_EX)) < 0) {
  973.     mm_log ("Unable to lock expunge mailbox",ERROR);
  974.     return;
  975.   }
  976.                 /* get exclusive access */
  977.   if (flock (LOCAL->fd,LOCK_EX|LOCK_NB)) {
  978.     flock (LOCAL->fd,LOCK_SH);    /* recover previous lock */
  979.     mm_log("Can't expunge because mailbox is in use by another process",ERROR);
  980.     mtx_unlock (ld,lock);    /* release exclusive parse/append permission */
  981.     return;
  982.   }
  983.  
  984.   mm_critical (stream);        /* go critical */
  985.   recent = stream->recent;    /* get recent now that pinged and locked */
  986.   while (i <= stream->nmsgs) {    /* for each message */
  987.                 /* number of bytes to smash or preserve */
  988.     k = ((elt = mtx_elt (stream,i))->data2 >> 24) + mtx_size (stream,i);
  989.     if (elt->deleted) {        /* if deleted */
  990.       if (elt->recent) --recent;/* if recent, note one less recent message */
  991.       delta += k;        /* number of bytes to delete */
  992.       mail_expunged (stream,i);    /* notify upper levels */
  993.       n++;            /* count up one more expunged message */
  994.     }
  995.     else if (i++ && delta) {    /* preserved message */
  996.       j = elt->data1;        /* first byte to preserve */
  997.       do {            /* read from source position */
  998.     m = min (k,LOCAL->buflen);
  999.     lseek (LOCAL->fd,j,L_SET);
  1000.     read (LOCAL->fd,LOCAL->buf,(unsigned int) m);
  1001.     pos = j - delta;    /* write to destination position */
  1002.     lseek (LOCAL->fd,pos,L_SET);
  1003.     write (LOCAL->fd,LOCAL->buf,(unsigned int) m);
  1004.     pos += m;        /* new position */
  1005.     j += m;            /* next chunk, perhaps */
  1006.       } while (k -= m);        /* until done */
  1007.       elt->data1 -= delta;    /* note the new address of this text */
  1008.     }
  1009.     else pos = elt->data1 + k;    /* preserved but no deleted messages */
  1010.   }
  1011.   if (n) {            /* truncate file after last message */
  1012.     if (pos != (LOCAL->filesize -= delta)) {
  1013.       sprintf (LOCAL->buf,"Calculated size mismatch %ld != %ld, delta = %ld",
  1014.            pos,LOCAL->filesize,delta);
  1015.       mm_log (LOCAL->buf,WARN);
  1016.       LOCAL->filesize = pos;    /* fix it then */
  1017.     }
  1018.     ftruncate (LOCAL->fd,LOCAL->filesize);
  1019.     sprintf (LOCAL->buf,"Expunged %ld messages",n);
  1020.                 /* output the news */
  1021.     mm_log (LOCAL->buf,(long) NIL);
  1022.   }
  1023.   else mm_log ("No messages deleted, so no update needed",(long) NIL);
  1024.   fsync (LOCAL->fd);        /* force disk update */
  1025.   fstat (LOCAL->fd,&sbuf);    /* get new write time */
  1026.   LOCAL->filetime = sbuf.st_mtime;
  1027.   mm_nocritical (stream);    /* release critical */
  1028.                 /* notify upper level of new mailbox size */
  1029.   mail_exists (stream,stream->nmsgs);
  1030.   mail_recent (stream,recent);
  1031.   flock (LOCAL->fd,LOCK_SH);    /* allow sharers again */
  1032.   mtx_unlock (ld,lock);    /* release exclusive parse/append permission */
  1033. }
  1034.  
  1035. /* Mtx mail copy message(s)
  1036.  * Accepts: MAIL stream
  1037.  *        sequence
  1038.  *        destination mailbox
  1039.  * Returns: T if success, NIL if failed
  1040.  */
  1041.  
  1042. long mtx_copy (MAILSTREAM *stream,char *sequence,char *mailbox)
  1043. {
  1044.                 /* copy the messages */
  1045.   return (mail_sequence (stream,sequence)) ?
  1046.     mtx_copy_messages (stream,mailbox) : NIL;
  1047. }
  1048.  
  1049.  
  1050. /* Mtx mail move message(s)
  1051.  * Accepts: MAIL stream
  1052.  *        sequence
  1053.  *        destination mailbox
  1054.  * Returns: T if success, NIL if failed
  1055.  */
  1056.  
  1057. long mtx_move (MAILSTREAM *stream,char *sequence,char *mailbox)
  1058. {
  1059.   long i;
  1060.   struct stat sbuf;
  1061.   MESSAGECACHE *elt;
  1062.   if (!(mail_sequence (stream,sequence) &&
  1063.     mtx_copy_messages (stream,mailbox))) return NIL;
  1064.                 /* delete all requested messages */
  1065.   for (i = 1; i <= stream->nmsgs; i++)
  1066.     if ((elt = mtx_elt (stream,i))->sequence) {
  1067.       elt->deleted = T;        /* mark message deleted */
  1068.                 /* recalculate status */
  1069.       mtx_update_status (stream,i,NIL);
  1070.     }
  1071.   if (!stream->rdonly) {    /* make sure the update takes */
  1072.     fsync (LOCAL->fd);
  1073.     fstat (LOCAL->fd,&sbuf);    /* get current write time */
  1074.     LOCAL->filetime = sbuf.st_mtime;
  1075.   }
  1076.   return LONGT;
  1077. }
  1078.  
  1079. /* Mtx mail append message from stringstruct
  1080.  * Accepts: MAIL stream
  1081.  *        destination mailbox
  1082.  *        stringstruct of messages to append
  1083.  * Returns: T if append successful, else NIL
  1084.  */
  1085.  
  1086. long mtx_append (MAILSTREAM *stream,char *mailbox,char *flags,char *date,
  1087.          STRING *message)
  1088. {
  1089.   struct stat sbuf;
  1090.   int fd,ld;
  1091.   char *s,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN];
  1092.   time_t tp[2];
  1093.   MESSAGECACHE elt;
  1094.   long i;
  1095.   long size = SIZE (message);
  1096.   long ret = LONGT;
  1097.   unsigned long uf = 0;
  1098.   short f = 0;
  1099.   if (flags) {            /* get flags if given */
  1100.     unsigned long ruf;
  1101.     f = mtx_getflags (user_flags (&mtxproto),flags,&ruf);
  1102.                 /* reverse bits (dontcha wish we had CIRC?) */
  1103.     while (ruf) uf |= 1 << (29 - find_rightmost_bit (&ruf));
  1104.   }
  1105.   if (date) {            /* want to preserve date? */
  1106.                 /* yes, parse date into an elt */
  1107.     if (!mail_parse_date (&elt,date)) {
  1108.       sprintf (tmp,"Bad date in append: %s",date);
  1109.       mm_log (tmp,ERROR);
  1110.       return NIL;
  1111.     }
  1112.   }
  1113.                 /* N.B.: can't use LOCAL->buf for tmp */
  1114.                 /* make sure valid mailbox */
  1115.   if (!mtx_isvalid (mailbox,tmp)) switch (errno) {
  1116.   case ENOENT:            /* no such file? */
  1117.     mm_notify (stream,"[TRYCREATE] Must create mailbox before append",NIL);
  1118.     return NIL;
  1119.   case 0:            /* merely empty file? */
  1120.     break;
  1121.   case EINVAL:
  1122.     sprintf (tmp,"Invalid MTX-format mailbox name: %s",mailbox);
  1123.     mm_log (tmp,ERROR);
  1124.     return NIL;
  1125.   default:
  1126.     sprintf (tmp,"Not a MTX-format mailbox: %s",mailbox);
  1127.     mm_log (tmp,ERROR);
  1128.     return NIL;
  1129.   }
  1130.   if ((fd = open (mtx_file (file,mailbox),O_RDWR|O_CREAT,
  1131.           S_IREAD|S_IWRITE)) < 0) {
  1132.     sprintf (tmp,"Can't open append mailbox: %s",strerror (errno));
  1133.     mm_log (tmp,ERROR);
  1134.     return NIL;
  1135.   }
  1136.                 /* get exclusive parse/append permission */
  1137.   if ((ld = mtx_lock (fd,lock,LOCK_EX)) < 0) {
  1138.     mm_log ("Unable to lock append mailbox",ERROR);
  1139.     return NIL;
  1140.   }
  1141.   s = (char *) fs_get (size);    /* copy message */
  1142.   for (i = 0; i < size; s[i++] = SNX (message));
  1143.  
  1144.   mm_critical (stream);        /* go critical */
  1145.   fstat (fd,&sbuf);        /* get current file size */
  1146.   lseek (fd,sbuf.st_size,L_SET);/* move to end of file */
  1147.   if (date) mail_date(tmp,&elt);/* write preseved date */
  1148.   else internal_date (tmp);    /* get current date in IMAP format */
  1149.                 /* add remainder of header */
  1150.   sprintf (tmp+26,",%ld;%010lo%02o\015\012",size,uf,f);
  1151.                 /* write header */
  1152.   if ((write (fd,tmp,strlen (tmp)) < 0) || ((write (fd,s,size)) < 0) ||
  1153.       fsync (fd)) {
  1154.     sprintf (tmp,"Message append failed: %s",strerror (errno));
  1155.     mm_log (tmp,ERROR);
  1156.     ftruncate (fd,sbuf.st_size);
  1157.     ret = NIL;
  1158.   }
  1159.   tp[0] = sbuf.st_atime;    /* preserve atime and mtime */
  1160.   tp[1] = sbuf.st_mtime;
  1161.   utime (file,tp);        /* set the times */
  1162.   mtx_unlock (ld,lock);        /* release exclusive parse/append permission */
  1163.   close (fd);            /* close the file */
  1164.   mm_nocritical (stream);    /* release critical */
  1165.   fs_give ((void **) &s);    /* flush the buffer */
  1166.   return ret;
  1167. }
  1168.  
  1169. /* Mtx garbage collect stream
  1170.  * Accepts: Mail stream
  1171.  *        garbage collection flags
  1172.  */
  1173.  
  1174. void mtx_gc (MAILSTREAM *stream,long gcflags)
  1175. {
  1176.   /* nothing here for now */
  1177. }
  1178.  
  1179. /* Internal routines */
  1180.  
  1181.  
  1182. /* Mtx mail lock file for parse/append permission
  1183.  * Accepts: file descriptor
  1184.  *        lock file name buffer
  1185.  *        type of locking operation (LOCK_SH or LOCK_EX)
  1186.  * Returns: file descriptor of lock or -1 if failure
  1187.  */
  1188.  
  1189. int mtx_lock (int fd,char *lock,int op)
  1190. {
  1191.   int ld;
  1192.   struct stat sbuf;
  1193.   if (!fstat (fd,&sbuf)) {    /* get data for this file */
  1194.                 /* make temporary file name */
  1195.     sprintf (lock,"/tmp/.%hx.%lx",sbuf.st_dev,sbuf.st_ino);
  1196.     if (chk_notsymlink (lock,T) &&
  1197.     ((ld = open (lock,O_RDWR|O_CREAT,
  1198.              (int)mail_parameters(NIL,GET_LOCKPROTECTION,NIL))) >= 0)){
  1199.       flock (ld,op);        /* get this lock */
  1200.       return ld;        /* return locking file descriptor */
  1201.     }
  1202.   }
  1203.   return -1;
  1204. }
  1205.  
  1206.  
  1207. /* Mtx mail unlock file for parse/append permission
  1208.  * Accepts: file descriptor
  1209.  *        lock file name from mtx_lock()
  1210.  */
  1211.  
  1212. void mtx_unlock (int fd,char *lock)
  1213. {
  1214.   unlink (lock);        /* delete the file */
  1215.   flock (fd,LOCK_UN);        /* unlock it */
  1216.   close (fd);            /* close it */
  1217. }
  1218.  
  1219. /* Mtx mail return internal message size in bytes
  1220.  * Accepts: MAIL stream
  1221.  *        message #
  1222.  * Returns: internal size of message
  1223.  */
  1224.  
  1225. unsigned long mtx_size (MAILSTREAM *stream,long m)
  1226. {
  1227.   MESSAGECACHE *elt = mail_elt (stream,m);
  1228.   return ((m < stream->nmsgs) ? mail_elt (stream,m+1)->data1 : LOCAL->filesize)
  1229.     - (elt->data1 + (elt->data2 >> 24));
  1230. }
  1231.  
  1232.  
  1233. /* Mtx mail generate file string
  1234.  * Accepts: temporary buffer to write into
  1235.  *        mailbox name string
  1236.  * Returns: local file string or NIL if failure
  1237.  */
  1238.  
  1239. char *mtx_file (char *dst,char *name)
  1240. {
  1241.   char tmp[MAILTMPLEN];
  1242.   char *s = mailboxfile (dst,name);
  1243.                 /* return our standard inbox */
  1244.   return (s && !*s) ? mailboxfile (dst,mtx_isvalid ("~/INBOX.MTX",tmp) ?
  1245.                    "INBOX.MTX" : "mail.txt") : s;
  1246. }
  1247.  
  1248. /* Parse flag list
  1249.  * Accepts: MAIL stream
  1250.  *        flag list as a character string
  1251.  *        pointer to user flags to return
  1252.  * Returns: system flags
  1253.  */
  1254.  
  1255. long mtx_getflags (MAILSTREAM *stream,char *flag,unsigned long *uf)
  1256. {
  1257.   char key[MAILTMPLEN];
  1258.   char *t,*s,tmp[MAILTMPLEN];
  1259.   short f = 0;
  1260.   long i;
  1261.   short j;
  1262.   *uf = 0;            /* initially no user flags */
  1263.   if (flag && *flag) {        /* no-op if no flag string */
  1264.                 /* check if a list and make sure valid */
  1265.     if ((i = (*flag == '(')) ^ (flag[strlen (flag)-1] == ')')) {
  1266.       mm_log ("Bad flag list",ERROR);
  1267.       return NIL;
  1268.     }
  1269.                 /* copy the flag string w/o list construct */
  1270.     strncpy (tmp,flag+i,(j = strlen (flag) - (2*i)));
  1271.     tmp[j] = '\0';        /* tie off tail */
  1272.                 /* make uppercase, find first, parse */
  1273.     if (t = strtok (ucase (tmp)," ")) do {
  1274.       i = 0;            /* no flag yet */
  1275.                 /* system flag, dispatch on first character */
  1276.       if (*t == '\\') switch (*++t) {
  1277.       case 'S':            /* possible \Seen flag */
  1278.     if (t[1] == 'E' && t[2] == 'E' && t[3] == 'N' && t[4] == '\0')
  1279.       f |= i = fSEEN;
  1280.     break;
  1281.       case 'D':            /* possible \Deleted flag */
  1282.     if (t[1] == 'E' && t[2] == 'L' && t[3] == 'E' && t[4] == 'T' &&
  1283.         t[5] == 'E' && t[6] == 'D' && t[7] == '\0') f |= i = fDELETED;
  1284.     break;
  1285.       case 'F':            /* possible \Flagged flag */
  1286.     if (t[1] == 'L' && t[2] == 'A' && t[3] == 'G' && t[4] == 'G' &&
  1287.         t[5] == 'E' && t[6] == 'D' && t[7] == '\0') f |= i = fFLAGGED;
  1288.     break;
  1289.       case 'A':            /* possible \Answered flag */
  1290.     if (t[1] == 'N' && t[2] == 'S' && t[3] == 'W' && t[4] == 'E' &&
  1291.         t[5] == 'R' && t[6] == 'E' && t[7] == 'D' && t[8] == '\0')
  1292.       f |= i = fANSWERED;
  1293.     break;
  1294.       default:            /* unknown */
  1295.     break;
  1296.       }
  1297.  
  1298.                 /* user flag, search through table */
  1299.       else for (j = 0; !i && j < NUSERFLAGS && (s =stream->user_flags[j]); ++j)
  1300.     if (!strcmp (t,ucase (strcpy (key,s)))) *uf |= i = 1 << j;
  1301.       if (!i) {            /* didn't find a matching flag? */
  1302.     sprintf (key,"Unknown flag: %.80s",t);
  1303.     mm_log (key,ERROR);
  1304.       }
  1305.                 /* parse next flag */
  1306.     } while (t = strtok (NIL," "));
  1307.   }
  1308.   return f;
  1309. }
  1310.  
  1311. /* Mtx mail parse mailbox
  1312.  * Accepts: MAIL stream
  1313.  * Returns: T if parse OK
  1314.  *        NIL if failure, stream aborted
  1315.  */
  1316.  
  1317. long mtx_parse (MAILSTREAM *stream)
  1318. {
  1319.   struct stat sbuf;
  1320.   MESSAGECACHE *elt = NIL;
  1321.   char c,*s,*t,*x;
  1322.   char tmp[MAILTMPLEN];
  1323.   unsigned long i,j,msiz;
  1324.   long curpos = LOCAL->filesize;
  1325.   long nmsgs = stream->nmsgs;
  1326.   long recent = stream->recent;
  1327.   fstat (LOCAL->fd,&sbuf);    /* get status */
  1328.   if (sbuf.st_size < curpos) {    /* sanity check */
  1329.     sprintf (tmp,"Mailbox shrank from %ld to %ld!",curpos,sbuf.st_size);
  1330.     mm_log (tmp,ERROR);
  1331.     mtx_close (stream);
  1332.     return NIL;
  1333.   }
  1334.   while (sbuf.st_size - curpos){/* while there is stuff to parse */
  1335.                 /* get to that position in the file */
  1336.     lseek (LOCAL->fd,curpos,L_SET);
  1337.     if ((i = read (LOCAL->fd,LOCAL->buf,64)) <= 0) {
  1338.       sprintf (tmp,"Unable to read internal header at %ld, size = %ld: %s",
  1339.            curpos,sbuf.st_size,i ? strerror (errno) : "no data read");
  1340.       mm_log (tmp,ERROR);
  1341.       mtx_close (stream);
  1342.       return NIL;
  1343.     }
  1344.     LOCAL->buf[i] = '\0';    /* tie off buffer just in case */
  1345.     if (!((s = strchr (LOCAL->buf,'\015')) && (s[1] == '\012'))) {
  1346.       sprintf (tmp,"Unable to find CRLF at %ld in %ld bytes, text: %s",
  1347.            curpos,i,LOCAL->buf);
  1348.       mm_log (tmp,ERROR);
  1349.       mtx_close (stream);
  1350.       return NIL;
  1351.     }
  1352.     *s = '\0';            /* tie off header line */
  1353.     i = (s + 2) - LOCAL->buf;    /* note start of text offset */
  1354.     if (!((s = strchr (LOCAL->buf,',')) && (t = strchr (s+1,';')))) {
  1355.       sprintf (tmp,"Unable to parse internal header at %ld: %s",curpos,
  1356.            LOCAL->buf);
  1357.       mm_log (tmp,ERROR);
  1358.       mtx_close (stream);
  1359.       return NIL;
  1360.     }
  1361.  
  1362.     *s++ = '\0'; *t++ = '\0';    /* tie off fields */
  1363.                 /* intantiate an elt for this message */
  1364.     (elt = mail_elt (stream,++nmsgs))->valid = T;
  1365.     elt->data1 = curpos;    /* note file offset of header */
  1366.     elt->data2 = i << 24;    /* as well as offset from header of message */
  1367.                 /* parse the header components */
  1368.     if (!(mail_parse_date (elt,LOCAL->buf) &&
  1369.       (elt->rfc822_size = msiz = strtol (x = s,&s,10)) && (!(s && *s)) &&
  1370.       isdigit (t[0]) && isdigit (t[1]) && isdigit (t[2]) &&
  1371.       isdigit (t[3]) && isdigit (t[4]) && isdigit (t[5]) &&
  1372.       isdigit (t[6]) && isdigit (t[7]) && isdigit (t[8]) &&
  1373.       isdigit (t[9]) && isdigit (t[10]) && isdigit (t[11]) && !t[12])) {
  1374.       sprintf (tmp,"Unable to parse internal header elements at %ld: %s,%s;%s",
  1375.            curpos,LOCAL->buf,x,t);
  1376.       mm_log (tmp,ERROR);
  1377.       mtx_close (stream);
  1378.       return NIL;
  1379.     }
  1380.                 /* make sure message fits in file */
  1381.     if ((curpos += (msiz + i)) > sbuf.st_size) {
  1382.       mm_log ("Last message runs past end of file",ERROR);
  1383.       mtx_close (stream);
  1384.       return NIL;
  1385.     }
  1386.     c = t[10];            /* remember first system flags byte */
  1387.     t[10] = '\0';        /* tie off flags */
  1388.     j = strtol (t,NIL,8);    /* get user flags value */
  1389.     t[10] = c;            /* restore first system flags byte */
  1390.                 /* set up all valid user flags (reversed!) */
  1391.     while (j) if (((i = 29 - find_rightmost_bit (&j)) < NUSERFLAGS) &&
  1392.           stream->user_flags[i]) elt->user_flags |= 1 << i;
  1393.                 /* calculate system flags */
  1394.     if ((j = ((t[10]-'0') * 8) + t[11]-'0') & fSEEN) elt->seen = T;
  1395.     if (j & fDELETED) elt->deleted = T;
  1396.     if (j & fFLAGGED) elt->flagged = T;
  1397.     if (j & fANSWERED) elt->answered = T;
  1398.     if (!(j & fOLD)) {        /* newly arrived message? */
  1399.       elt->recent = T;
  1400.       recent++;            /* count up a new recent message */
  1401.                 /* mark it as old */
  1402.       mtx_update_status (stream,nmsgs,NIL);
  1403.     }
  1404.   }
  1405.   fsync (LOCAL->fd);        /* make sure all the fOLD flags take */
  1406.                 /* update parsed file size and time */
  1407.   LOCAL->filesize = sbuf.st_size;
  1408.   fstat (LOCAL->fd,&sbuf);    /* get status again to ensure time is right */
  1409.   LOCAL->filetime = sbuf.st_mtime;
  1410.   mail_exists (stream,nmsgs);    /* notify upper level of new mailbox size */
  1411.   mail_recent (stream,recent);    /* and of change in recent messages */
  1412.   return LONGT;            /* return the winnage */
  1413. }
  1414.  
  1415. /* Mtx copy messages
  1416.  * Accepts: MAIL stream
  1417.  *        mailbox copy vector
  1418.  *        mailbox name
  1419.  * Returns: T if success, NIL if failed
  1420.  */
  1421.  
  1422. long mtx_copy_messages (MAILSTREAM *stream,char *mailbox)
  1423. {
  1424.   struct stat sbuf;
  1425.   time_t tp[2];
  1426.   MESSAGECACHE *elt;
  1427.   unsigned long i,j,k;
  1428.   long ret = LONGT;
  1429.   int fd,ld;
  1430.   char file[MAILTMPLEN],lock[MAILTMPLEN];
  1431.                 /* make sure valid mailbox */
  1432.   if (!mtx_isvalid (mailbox,LOCAL->buf)) switch (errno) {
  1433.   case ENOENT:            /* no such file? */
  1434.     mm_notify (stream,"[TRYCREATE] Must create mailbox before copy",NIL);
  1435.     return NIL;
  1436.   case 0:            /* merely empty file? */
  1437.     break;
  1438.   case EINVAL:
  1439.     sprintf (LOCAL->buf,"Invalid MTX-format mailbox name: %s",mailbox);
  1440.     mm_log (LOCAL->buf,ERROR);
  1441.     return NIL;
  1442.   default:
  1443.     sprintf (LOCAL->buf,"Not a MTX-format mailbox: %s",mailbox);
  1444.     mm_log (LOCAL->buf,ERROR);
  1445.     return NIL;
  1446.   }
  1447.                 /* got file? */  
  1448.   if ((fd = open (mtx_file (file,mailbox),O_RDWR|O_CREAT,S_IREAD|S_IWRITE))<0){
  1449.     sprintf (LOCAL->buf,"Unable to open copy mailbox: %s",strerror (errno));
  1450.     mm_log (LOCAL->buf,ERROR);
  1451.     return NIL;
  1452.   }
  1453.   mm_critical (stream);        /* go critical */
  1454.                 /* get exclusive parse/append permission */
  1455.   if ((ld = mtx_lock (fd,lock,LOCK_EX)) < 0) {
  1456.     mm_log ("Unable to lock copy mailbox",ERROR);
  1457.     return NIL;
  1458.   }
  1459.   fstat (fd,&sbuf);        /* get current file size */
  1460.   lseek (fd,sbuf.st_size,L_SET);/* move to end of file */
  1461.  
  1462.                 /* for each requested message */
  1463.   for (i = 1; ret && (i <= stream->nmsgs); i++) 
  1464.     if ((elt = mail_elt (stream,i))->sequence) {
  1465.       lseek (LOCAL->fd,elt->data1,L_SET);
  1466.                 /* number of bytes to copy */
  1467.       k = (elt->data2 >> 24) + mtx_size (stream,i);
  1468.       do {            /* read from source position */
  1469.     j = min (k,LOCAL->buflen);
  1470.     read (LOCAL->fd,LOCAL->buf,(unsigned int) j);
  1471.     if ((write (fd,LOCAL->buf,(unsigned int) j) < 0) || fsync (fd)) {
  1472.       sprintf (LOCAL->buf,"Unable to write message: %s",strerror (errno));
  1473.       mm_log (LOCAL->buf,ERROR);
  1474.       ftruncate (fd,sbuf.st_size);
  1475.       j = k;
  1476.       ret = NIL;        /* note error */
  1477.       break;
  1478.     }
  1479.       } while (k -= j);        /* until done */
  1480.     }
  1481.   tp[0] = sbuf.st_atime;    /* preserve atime and mtime */
  1482.   tp[1] = sbuf.st_mtime;
  1483.   utime (file,tp);        /* set the times */
  1484.   mtx_unlock (ld,lock);        /* release exclusive parse/append permission */
  1485.   close (fd);            /* close the file */
  1486.   mm_nocritical (stream);    /* release critical */
  1487.   return ret;
  1488. }
  1489.  
  1490. /* Mtx get cache element with status updating from file
  1491.  * Accepts: MAIL stream
  1492.  *        message number
  1493.  * Returns: cache element
  1494.  */
  1495.  
  1496. MESSAGECACHE *mtx_elt (MAILSTREAM *stream,long msgno)
  1497. {
  1498.   unsigned long i,j,sysflags;
  1499.   char c;
  1500.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  1501.   unsigned long oldsysflags = (elt->seen ? fSEEN : NIL) |
  1502.     (elt->deleted ? fDELETED : NIL) | (elt->flagged ? fFLAGGED : NIL) |
  1503.       (elt->answered ? fANSWERED : NIL);
  1504.   unsigned long olduserflags = elt->user_flags;
  1505.                 /* set the seek pointer */
  1506.   lseek (LOCAL->fd,(off_t) elt->data1 + (elt->data2 >> 24) - 14,L_SET);
  1507.                 /* read the new flags */
  1508.   if (read (LOCAL->fd,LOCAL->buf,12) < 0) {
  1509.     sprintf (LOCAL->buf,"Unable to read new status: %s",strerror (errno));
  1510.     fatal (LOCAL->buf);
  1511.   }
  1512.                 /* calculate system flags */
  1513.   sysflags = (((LOCAL->buf[10]-'0') * 8) + LOCAL->buf[11]-'0') &
  1514.     (fSEEN|fDELETED|fFLAGGED|fANSWERED);
  1515.   elt->seen = sysflags & fSEEN ? T : NIL;
  1516.   elt->deleted = sysflags & fDELETED ? T : NIL;
  1517.   elt->flagged = sysflags & fFLAGGED ? T : NIL;
  1518.   elt->answered = sysflags & fANSWERED ? T : NIL;
  1519.   c = LOCAL->buf[10];        /* remember first system flags byte */
  1520.   LOCAL->buf[10] = '\0';    /* tie off flags */
  1521.   j = strtol (LOCAL->buf,NIL,8);/* get user flags value */
  1522.   LOCAL->buf[10] = c;        /* restore first system flags byte */
  1523.                 /* set up all valid user flags (reversed!) */
  1524.   while (j) if (((i = 29 - find_rightmost_bit (&j)) < NUSERFLAGS) &&
  1525.         stream->user_flags[i]) elt->user_flags |= 1 << i;
  1526.   if ((oldsysflags != sysflags) || (olduserflags != elt->user_flags))
  1527.     mm_flags (stream,msgno);    /* let top level know */
  1528.   return elt;
  1529. }
  1530.  
  1531. /* Mtx update status string
  1532.  * Accepts: MAIL stream
  1533.  *        message number
  1534.  *        flag saying whether or not to sync
  1535.  */
  1536.  
  1537. void mtx_update_status (MAILSTREAM *stream,long msgno,long syncflag)
  1538. {
  1539.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  1540.   struct stat sbuf;
  1541.   unsigned long j,k = 0;
  1542.   if (!stream->rdonly) {    /* not if readonly you don't */
  1543.     j = elt->user_flags;    /* get user flags */
  1544.                 /* reverse bits (dontcha wish we had CIRC?) */
  1545.     while (j) k |= 1 << (29 - find_rightmost_bit (&j));
  1546.                 /* print new flag string */
  1547.     sprintf (LOCAL->buf,"%010lo%02o",k,
  1548.          fOLD + (fSEEN * elt->seen) + (fDELETED * elt->deleted) +
  1549.          (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered));
  1550.                 /* get to that place in the file */
  1551.     lseek (LOCAL->fd,(off_t) elt->data1 + (elt->data2 >> 24) - 14,L_SET);
  1552.                 /* write new flags */
  1553.     write (LOCAL->fd,LOCAL->buf,12);
  1554.                 /* sync if requested */
  1555.     if (syncflag) fsync (LOCAL->fd);
  1556.     fstat (LOCAL->fd,&sbuf);    /* get new write time */
  1557.     LOCAL->filetime = sbuf.st_mtime;
  1558.   }
  1559. }
  1560.  
  1561. /* Search support routines
  1562.  * Accepts: MAIL stream
  1563.  *        message number
  1564.  *        pointer to additional data
  1565.  * Returns: T if search matches, else NIL
  1566.  */
  1567.  
  1568.  
  1569. char mtx_search_all (MAILSTREAM *stream,long msgno,char *d,long n)
  1570. {
  1571.   return T;            /* ALL always succeeds */
  1572. }
  1573.  
  1574.  
  1575. char mtx_search_answered (MAILSTREAM *stream,long msgno,char *d,long n)
  1576. {
  1577.   return mail_elt (stream,msgno)->answered ? T : NIL;
  1578. }
  1579.  
  1580.  
  1581. char mtx_search_deleted (MAILSTREAM *stream,long msgno,char *d,long n)
  1582. {
  1583.   return mail_elt (stream,msgno)->deleted ? T : NIL;
  1584. }
  1585.  
  1586.  
  1587. char mtx_search_flagged (MAILSTREAM *stream,long msgno,char *d,long n)
  1588. {
  1589.   return mail_elt (stream,msgno)->flagged ? T : NIL;
  1590. }
  1591.  
  1592.  
  1593. char mtx_search_keyword (MAILSTREAM *stream,long msgno,char *d,long n)
  1594. {
  1595.   return mail_elt (stream,msgno)->user_flags & n ? T : NIL;
  1596. }
  1597.  
  1598.  
  1599. char mtx_search_new (MAILSTREAM *stream,long msgno,char *d,long n)
  1600. {
  1601.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  1602.   return (elt->recent && !elt->seen) ? T : NIL;
  1603. }
  1604.  
  1605. char mtx_search_old (MAILSTREAM *stream,long msgno,char *d,long n)
  1606. {
  1607.   return mail_elt (stream,msgno)->recent ? NIL : T;
  1608. }
  1609.  
  1610.  
  1611. char mtx_search_recent (MAILSTREAM *stream,long msgno,char *d,long n)
  1612. {
  1613.   return mail_elt (stream,msgno)->recent ? T : NIL;
  1614. }
  1615.  
  1616.  
  1617. char mtx_search_seen (MAILSTREAM *stream,long msgno,char *d,long n)
  1618. {
  1619.   return mail_elt (stream,msgno)->seen ? T : NIL;
  1620. }
  1621.  
  1622.  
  1623. char mtx_search_unanswered (MAILSTREAM *stream,long msgno,char *d,long n)
  1624. {
  1625.   return mail_elt (stream,msgno)->answered ? NIL : T;
  1626. }
  1627.  
  1628.  
  1629. char mtx_search_undeleted (MAILSTREAM *stream,long msgno,char *d,long n)
  1630. {
  1631.   return mail_elt (stream,msgno)->deleted ? NIL : T;
  1632. }
  1633.  
  1634.  
  1635. char mtx_search_unflagged (MAILSTREAM *stream,long msgno,char *d,long n)
  1636. {
  1637.   return mail_elt (stream,msgno)->flagged ? NIL : T;
  1638. }
  1639.  
  1640.  
  1641. char mtx_search_unkeyword (MAILSTREAM *stream,long msgno,char *d,long n)
  1642. {
  1643.   return mail_elt (stream,msgno)->user_flags & n ? NIL : T;
  1644. }
  1645.  
  1646.  
  1647. char mtx_search_unseen (MAILSTREAM *stream,long msgno,char *d,long n)
  1648. {
  1649.   return mail_elt (stream,msgno)->seen ? NIL : T;
  1650. }
  1651.  
  1652. char mtx_search_before (MAILSTREAM *stream,long msgno,char *d,long n)
  1653. {
  1654.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  1655.   return (char) ((long)((elt->year << 9) + (elt->month << 5) + elt->day) < n);
  1656. }
  1657.  
  1658.  
  1659. char mtx_search_on (MAILSTREAM *stream,long msgno,char *d,long n)
  1660. {
  1661.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  1662.   return (char) (((elt->year << 9) + (elt->month << 5) + elt->day) == n);
  1663. }
  1664.  
  1665.  
  1666. char mtx_search_since (MAILSTREAM *stream,long msgno,char *d,long n)
  1667. {
  1668.                 /* everybody interprets "since" as .GE. */
  1669.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  1670.   return (char) ((long)((elt->year << 9) + (elt->month << 5) + elt->day) >= n);
  1671. }
  1672.  
  1673.  
  1674. char mtx_search_body (MAILSTREAM *stream,long msgno,char *d,long n)
  1675. {
  1676.   char ret;
  1677.   unsigned long hdrsize;
  1678.   unsigned long hdrpos = mtx_header (stream,msgno,&hdrsize);
  1679.   unsigned long textsize = mtx_size (stream,msgno) - hdrsize;
  1680.   char *s = (char *) fs_get (1 + textsize);
  1681.   s[textsize] = '\0';        /* tie off string */
  1682.                 /* recalculate status */
  1683.   mtx_update_status (stream,msgno,T);
  1684.                 /* get to text position */
  1685.   lseek (LOCAL->fd,hdrpos + hdrsize,L_SET);
  1686.   read (LOCAL->fd,s,textsize);    /* slurp the data */
  1687.                 /* copy the string */
  1688.   ret = search (s,textsize,d,n);/* do the search */
  1689.   fs_give ((void **) &s);    /* flush readin buffer */
  1690.   return ret;            /* return search value */
  1691. }
  1692.  
  1693.  
  1694. char mtx_search_subject (MAILSTREAM *stream,long msgno,char *d,long n)
  1695. {
  1696.   char *s = mtx_fetchstructure (stream,msgno,NIL)->subject;
  1697.   return s ? search (s,(long) strlen (s),d,n) : NIL;
  1698. }
  1699.  
  1700.  
  1701. char mtx_search_text (MAILSTREAM *stream,long msgno,char *d,long n)
  1702. {
  1703.   char ret;
  1704.   unsigned long hdrsize;
  1705.   unsigned long hdrpos = mtx_header (stream,msgno,&hdrsize);
  1706.   char *s = (char *) fs_get (1 + hdrsize);
  1707.   s[hdrsize] = '\0';        /* tie off string */
  1708.                 /* get to header position */
  1709.   lseek (LOCAL->fd,hdrpos,L_SET);
  1710.   read (LOCAL->fd,s,hdrsize);    /* slurp the data */
  1711.   ret = search (s,hdrsize,d,n) || mtx_search_body (stream,msgno,d,n);
  1712.   fs_give ((void **) &s);    /* flush readin buffer */
  1713.   return ret;            /* return search value */
  1714. }
  1715.  
  1716. char mtx_search_bcc (MAILSTREAM *stream,long msgno,char *d,long n)
  1717. {
  1718.   ADDRESS *a = mtx_fetchstructure (stream,msgno,NIL)->bcc;
  1719.   LOCAL->buf[0] = '\0';        /* initially empty string */
  1720.                 /* get text for address */
  1721.   rfc822_write_address (LOCAL->buf,a);
  1722.   return search (LOCAL->buf,(long) strlen (LOCAL->buf),d,n);
  1723. }
  1724.  
  1725.  
  1726. char mtx_search_cc (MAILSTREAM *stream,long msgno,char *d,long n)
  1727. {
  1728.   ADDRESS *a = mtx_fetchstructure (stream,msgno,NIL)->cc;
  1729.   LOCAL->buf[0] = '\0';        /* initially empty string */
  1730.                 /* get text for address */
  1731.   rfc822_write_address (LOCAL->buf,a);
  1732.   return search (LOCAL->buf,(long) strlen (LOCAL->buf),d,n);
  1733. }
  1734.  
  1735.  
  1736. char mtx_search_from (MAILSTREAM *stream,long msgno,char *d,long n)
  1737. {
  1738.   ADDRESS *a = mtx_fetchstructure (stream,msgno,NIL)->from;
  1739.   LOCAL->buf[0] = '\0';        /* initially empty string */
  1740.                 /* get text for address */
  1741.   rfc822_write_address (LOCAL->buf,a);
  1742.   return search (LOCAL->buf,(long) strlen (LOCAL->buf),d,n);
  1743. }
  1744.  
  1745.  
  1746. char mtx_search_to (MAILSTREAM *stream,long msgno,char *d,long n)
  1747. {
  1748.   ADDRESS *a = mtx_fetchstructure (stream,msgno,NIL)->to;
  1749.   LOCAL->buf[0] = '\0';        /* initially empty string */
  1750.                 /* get text for address */
  1751.   rfc822_write_address (LOCAL->buf,a);
  1752.   return search (LOCAL->buf,(long) strlen (LOCAL->buf),d,n);
  1753. }
  1754.  
  1755. /* Search parsers */
  1756.  
  1757.  
  1758. /* Parse a date
  1759.  * Accepts: function to return
  1760.  *        pointer to date integer to return
  1761.  * Returns: function to return
  1762.  */
  1763.  
  1764. search_t mtx_search_date (search_t f,long *n)
  1765. {
  1766.   long i;
  1767.   char *s;
  1768.   MESSAGECACHE elt;
  1769.                 /* parse the date and return fn if OK */
  1770.   return (mtx_search_string (f,&s,&i) && mail_parse_date (&elt,s) &&
  1771.       (*n = (elt.year << 9) + (elt.month << 5) + elt.day)) ? f : NIL;
  1772. }
  1773.  
  1774. /* Parse a flag
  1775.  * Accepts: function to return
  1776.  *        pointer to keyword integer to return
  1777.  *        MAIL stream
  1778.  * Returns: function to return
  1779.  */
  1780.  
  1781. search_t mtx_search_flag (search_t f,long *n,MAILSTREAM *stream)
  1782. {
  1783.   short i;
  1784.   char *s,*t;
  1785.   if (t = strtok (NIL," ")) {    /* get a keyword */
  1786.     ucase (t);            /* get uppercase form of flag */
  1787.     for (i = 0; i < NUSERFLAGS && (s = stream->user_flags[i]); ++i)
  1788.       if (!strcmp (t,ucase (strcpy (LOCAL->buf,s))) && (*n = 1 << i)) return f;
  1789.   }
  1790.   return NIL;            /* couldn't find keyword */
  1791. }
  1792.  
  1793. /* Parse a string
  1794.  * Accepts: function to return
  1795.  *        pointer to string to return
  1796.  *        pointer to string length to return
  1797.  * Returns: function to return
  1798.  */
  1799.  
  1800.  
  1801. search_t mtx_search_string (search_t f,char **d,long *n)
  1802. {
  1803.   char *end = " ";
  1804.   char *c = strtok (NIL,"");    /* remainder of criteria */
  1805.   if (!c) return NIL;        /* missing argument */
  1806.   switch (*c) {            /* see what the argument is */
  1807.   case '{':            /* literal string */
  1808.     *n = strtol (c+1,d,10);    /* get its length */
  1809.     if ((*(*d)++ == '}') && (*(*d)++ == '\015') && (*(*d)++ == '\012') &&
  1810.     (!(*(c = *d + *n)) || (*c == ' '))) {
  1811.       char e = *--c;
  1812.       *c = DELIM;        /* make sure not a space */
  1813.       strtok (c," ");        /* reset the strtok mechanism */
  1814.       *c = e;            /* put character back */
  1815.       break;
  1816.     }
  1817.   case '\0':            /* catch bogons */
  1818.   case ' ':
  1819.     return NIL;
  1820.   case '"':            /* quoted string */
  1821.     if (strchr (c+1,'"')) end = "\"";
  1822.     else return NIL;
  1823.   default:            /* atomic string */
  1824.     if (*d = strtok (c,end)) *n = strlen (*d);
  1825.     else return NIL;
  1826.     break;
  1827.   }
  1828.   return f;
  1829. }
  1830.